/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "vsf.h"

static struct vsfusbd_UVC_control_t *vsfusbd_UVC_get_control(
		struct vsfusbd_UVC_t *uvc, uint8_t entity, uint8_t selector)
{
	struct vsfusbd_UVC_control_t *control = uvc->control;
	for (uint32_t i = 0; i < uvc->control_num; i++, control++)
		if ((control->info->entity == entity) && (control->info->selector == selector))
			return control;
	return NULL;
}

static struct vsfav_control_value_t *vsfusbd_UVC_get_value(
		struct vsfusbd_UVC_control_t *control, uint8_t request)
{
	struct vsfav_control_value_t *value = NULL;
	request = request & ~USB_UVCREQ_GET;
	switch (request)
	{
	case USB_UVCREQ_CUR:	value = &control->cur; break;
	case USB_UVCREQ_MIN:	value = (struct vsfav_control_value_t *)&control->info->min; break;
	case USB_UVCREQ_MAX:	value = (struct vsfav_control_value_t *)&control->info->max; break;
	case USB_UVCREQ_DEF:	value = (struct vsfav_control_value_t *)&control->info->def; break;
	}
	return value;
}

static vsf_err_t vsfusbd_UVC_request_prepare(struct vsfusbd_device_t *device,
		struct vsfusbd_iface_t *ifs)
{
	struct vsfusbd_UVC_t *uvc = (struct vsfusbd_UVC_t *)ifs->protocol_param;
	struct vsfusbd_ctrl_handler_t *ctrl_handler = &device->ctrl_handler;
	struct vsf_buffer_t *buffer = &ctrl_handler->bufstream.mem.buffer;
	struct usb_ctrlrequest_t *request = &ctrl_handler->request;
	uint8_t entity = (request->wIndex >> 8) & 0xFF;
	const struct vsfusbd_UVC_control_info_t *cinfo;
	struct vsfusbd_UVC_control_t *control;
	struct vsfav_control_value_t *value;

	switch (request->bRequestType & USB_RECIP_MASK)
	{
	case USB_RECIP_INTERFACE:
		switch (request->bRequestType & USB_TYPE_MASK)
		{
		case USB_TYPE_STANDARD:
			switch (request->bRequest)
			{
			case USB_REQ_SET_INTERFACE:
				break;
			}
			break;
		case USB_TYPE_CLASS:
			control = vsfusbd_UVC_get_control(uvc, entity, request->wValue);
			if (!control) return VSFERR_FAIL;

			cinfo = control->info;
			value = vsfusbd_UVC_get_value(control, request->bRequest);
			if (request->bRequest & USB_UVCREQ_GET)
			{
				if (!value)
				{
					switch (request->bRequest & ~USB_UVCREQ_GET)
					{
					case USB_UVCREQ_LEN:
						buffer->size = sizeof(control->info->size);
						// TODO: code below only support little-endian
						buffer->buffer = (uint8_t *)&cinfo->size;
						break;
					case USB_UVCREQ_RES:
					case USB_UVCREQ_INFO:
						return VSFERR_FAIL;
					}
				}
				else
				{
					buffer->size = cinfo->size;
					buffer->buffer = buffer->size < 4 ? &control->cur.uval8 : control->cur.pbuf;
				}
			}
			else
			{
				if (request->bRequest != (USB_UVCREQ_SET | USB_UVCREQ_CUR))
					return VSFERR_FAIL;

				buffer->size = cinfo->size;
				buffer->buffer = buffer->size < 4 ? &control->cur.uval8 : control->cur.pbuf;
			}
			break;
		}
		break;
	case USB_RECIP_ENDPOINT:
		break;
	}
	ctrl_handler->data_size = buffer->size;
	return VSFERR_NONE;
}

static vsf_err_t vsfusbd_UVC_request_process(struct vsfusbd_device_t *device,
		struct vsfusbd_iface_t *ifs)
{
	struct vsfusbd_UVC_t *uvc = (struct vsfusbd_UVC_t *)ifs->protocol_param;
	struct vsfusbd_ctrl_handler_t *ctrl_handler = &device->ctrl_handler;
	struct usb_ctrlrequest_t *request = &ctrl_handler->request;
	uint8_t entity = (request->wIndex >> 8) & 0xFF;
	struct vsfusbd_UVC_control_t *control;

	switch (request->bRequestType & USB_RECIP_MASK)
	{
	case USB_RECIP_INTERFACE:
		switch (request->bRequestType & USB_TYPE_MASK)
		{
		case USB_TYPE_STANDARD:
			switch (request->bRequest)
			{
			case USB_REQ_SET_INTERFACE:
				break;
			}
			break;
		case USB_TYPE_CLASS:
			control = vsfusbd_UVC_get_control(uvc, entity, request->wValue);
			if (!control) return VSFERR_FAIL;

			if ((request->bRequest == (USB_UVCREQ_SET | USB_UVCREQ_CUR)) &&
				(control->info->on_set != NULL))
			{
				control->info->on_set(control);
			}
			break;
		}
		break;
	case USB_RECIP_ENDPOINT:
		break;
	}
	return VSFERR_NONE;
}

static struct vsfsm_state_t *
vsfusbd_UVC_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
	struct vsfusbd_UVC_t *uvc = (struct vsfusbd_UVC_t *)sm->user_data;
	return NULL;
}

static vsf_err_t vsfusbd_UVCVC_class_init(struct vsfusbd_device_t *device,
		struct vsfusbd_iface_t *ifs)
{
	struct vsfusbd_UVC_t *uvc = (struct vsfusbd_UVC_t *)ifs->protocol_param;

	ifs->sm.init_state.evt_handler = vsfusbd_UVC_evt_handler;
	uvc->iface = ifs;
	uvc->device = device;
	ifs->sm.user_data = (void*)uvc;
	return vsfsm_init(&ifs->sm);
}

static vsf_err_t vsfusbd_UVCVS_class_init(struct vsfusbd_device_t *device,
		struct vsfusbd_iface_t *ifs)
{
	struct vsfusbd_UVC_t *uvc = (struct vsfusbd_UVC_t *)ifs->protocol_param;

//	uvc->video_stream->callback_rx.param = uvc;
//	uvc->video_stream->callback_rx.on_inout = vsfusbd_UVC_on_inout;
//	return vsfstream_connect_rx(
	return VSFERR_NONE;
}

const struct vsfusbd_class_protocol_t vsfusbd_UVCVC_class =
{
	.request_prepare =	vsfusbd_UVC_request_prepare,
	.request_process =	vsfusbd_UVC_request_process,
	.init =				vsfusbd_UVCVC_class_init,
};

const struct vsfusbd_class_protocol_t vsfusbd_UVCVS_class =
{
	.request_prepare =	vsfusbd_UVC_request_prepare,
	.request_process =	vsfusbd_UVC_request_process,
	.init =				vsfusbd_UVCVS_class_init,
};
